#include "simple_text_editor.h"
#include <cctype>
#include <algorithm>
#include <sstream>
Text_iterator &Text_iterator::operator++()
{
    ++pos;
    if (pos == ln->end())
    {
        ++ln;
        pos = ln->begin();
    }
    return *this;
}
Document::iterator Document::begin()
{
    return {line.begin(), line.begin()->begin()};
}
Document::iterator Document::end()
{
    auto last = line.end();
    --last;
    return {last, last->end()};
}
Document::iterator Document::insert(Document::iterator p, Document::iterator first, Document::iterator last)
{
    if (first == last)
        return p;
    int index = p.position() - p.line()->begin();
    Line remaining(p.position(), p.line()->end());
    p.line()->erase(p.position(), p.line()->end());
    auto it = p.line();
    for (; first != last; ++first)
    {
        it->push_back(*first);
        if (*first == '\n')
        {
            it = line.emplace(std::next(it));
        }
    }
    it->insert(it->end(), remaining.begin(), remaining.end());
    if (!line.back().empty())
    {
        line.emplace_back();
    }
    return {p.line(), p.line()->begin() + index};
}
Document::iterator Document::insert(Document::iterator p, const std::string &s)
{
    Document d = create_document(s);
    return insert(p, d.begin(), d.end());
}

Document::iterator Document::erase(Document::iterator first, Document::iterator last)
{
    if (first.line() == last.line())
    {
        return {first.line(), first.line()->erase(first.position(), last.position())};
    }
    if (last == end())
    {
        first.line()->erase(first.position(), first.line()->end());
        line.erase(std::next(first.line()), last.line());
        if (first.line()->empty())
            line.erase(first.line());
        return end();
    }
    first.line()->erase(first.position(), first.line()->end());
    auto pos = first.line()->insert(first.line()->end(), last.position(), last.line()->end());
    line.erase(std::next(first.line()), std::next(last.line()));
    return {first.line(), pos};
}
int Document::replace_all(const std::string &target, const std::string &replacement)
{
    int count = 0;
    for (auto it = begin(); it != end();)
    {
        if ((it = find_txt(it, end(), target)) == end())
        {
            break;
        }
        it = insert(erase(it, std::next(it, target.length())), replacement);
        std::advance(it, replacement.length());
        ++count;
    }
    return count;
}
std::istream &operator>>(std::istream &is, Document &d)
{
    for (char ch; is.get(ch);)
    {
        d.line.back().push_back(ch);
        if (ch == '\n')
        {
            d.line.emplace_back();
        }
    }
    if (!d.line.back().empty())
    {
        d.line.emplace_back();
    }
    return is;
}

std::ostream &operator<<(std::ostream &os, Document &d)
{
    for (auto c : d)
        os << c;
    return os;
}
Document create_document(const std::string &text)
{
    Document d;
    std::istringstream iss(text);
    iss >> d;
    return d;
}

void erase_line(Document &d, int n)
{
    if (n < 0 || n >= d.line.size() - 1)
        return;
    d.line.erase(std::next(d.line.begin(), n));
}

void erase_empty_lines(Document &doc)
{
    for (std::list<Line>::iterator pos = doc.begin().line(); pos != doc.end().line();)
    {
        if (pos->empty())
        {
            Document::iterator first{pos, pos->begin()};
            Document::iterator last{pos, pos->end()};
            pos = doc.erase(first, last).line();
        }
    }
}
Text_iterator find_txt(Text_iterator first, Text_iterator last, const std::string &s)
{
    if (s.empty())
        return last;
    return std::search(first, last, s.begin(), s.end());
}
int character_count(Text_iterator first, Text_iterator last)
{
    return std::distance(first, last);
}
int word_count(Text_iterator first, Text_iterator last, std::function<bool(char)> is_whitespace)
{
    int n = 0;
    for (bool in_word = false; first != last; ++first)
    {
        if (is_whitespace(*first))
        {
            in_word = false;
        }
        else if (!in_word)
        {
            in_word = true;
            ++n;
        }
    }
    return n;
}
int word_count(Text_iterator first, Text_iterator last)
{
    return word_count(first, last, [](char c) -> bool
                      { return isspace(c); });
}
int word_count(Text_iterator first, Text_iterator last, const std::string &whitespaces)
{
    return word_count(first, last, [&whitespaces](char c)
                      { return whitespaces.find(c) != std::string::npos; });
}